import { Page } from "puppeteer";
import Site from "../common/site";
import { logger } from "../utils/Logger";
import { scrapeSleep } from "../utils/index";
import prisma from "../db/prisma";
import { log } from "console";

export default class JDJob extends Site {
  cookies: any = null;
  constructor(url: string) {
    super(url);
  }

  loadCookiesFromFile = async (page: Page, filePath: String) => {
    try {
      // Reading cookies from the specified file
      const fs = require("fs");
      const cookiesJson = fs.readFileSync(filePath, "utf-8");
      const cookies = JSON.parse(cookiesJson);

      // Setting the cookies in the current page
      await page.setCookie(...cookies);
      // Cookies have been loaded successfully
      return true;
    } catch (error) {
      // An error occurred while loading cookies
      console.error("Error loading cookies:", error);
      return false;
    }
  };
  autoScroll = async (page: Page) => {
    await page.evaluate(async () => {
      await new Promise((resolve) => {
        var totalHeight = 0;
        var distance = 100;
        var timer = setInterval(() => {
          var scrollHeight = document.body.scrollHeight;
          window.scrollBy(0, distance);
          totalHeight += distance;

          if (totalHeight >= scrollHeight - window.innerHeight) {
            clearInterval(timer);
            resolve(true);
          }
        }, 100);
      });
    });
  };

  async gotoSearchRet(key_words: string): Promise<Page> {

    const browser = await this.launchBrowser();
    await browser.setCookie(
      ...(await this.getCookieFromFile("./search.jd.com.cookies.json")),
    );
    let page = await browser.newPage();
    await page.goto("https://search.jd.com", { waitUntil: "networkidle2" });
    try {
      await page.type("input.input_text", key_words);
      await scrapeSleep();
      await page.click("input.input_submit");
      await scrapeSleep();
      await page.waitForNavigation({ waitUntil: "load" });
    }
    catch (err) {
      logger.error(err);
      await page.screenshot({ path: "./search-result.jpg" });
    }
    return page;
  }

  scrape = async () => {

    const key_words = "4060";
    const searchResultPage = await this.gotoSearchRet(key_words);

    // const newPagePromise = new Promise<Page | null>((x) =>
    //   browser.once("targetcreated", (target) => x(target.page())),
    // );
    let skuIdList: string[] = [];
    let pageCount = 1;
    while (true) {
      logger.debug(`获取第${pageCount} 页数据`);
      // 通过sku id 访问到具体页面
      let skuIds = await this.scrapeSearchResultPage(searchResultPage, key_words);
      skuIdList.push(...(skuIds || []));
      logger.info(`当前skuIds共 ${skuIdList.length}, 列表: ${skuIdList}`);

      pageCount++;

      if (pageCount > 5) {
        // 只看前5页
        break;
      }
      //跳转到下一页
      let hasNext = (await searchResultPage.$("a.fp-next")) != null;
      let isLastPage = (await searchResultPage.$("a.fp-next.disabled")) != null;

      if (!hasNext) {
        logger.info("没有下一页");
        break;
      }
      if (isLastPage) {
        logger.info("当前已经是最后一页数据");
        break;
      }
      await scrapeSleep();
      try {
        await searchResultPage.click("a.fp-next");
        await searchResultPage.waitForNavigation({ waitUntil: "networkidle2" });
        await scrapeSleep();
        await searchResultPage.waitForSelector("a.fp-next", { visible: true });
        hasNext = (await searchResultPage.$("a.fp-next")) != null;
        isLastPage = (await searchResultPage.$("a.fp-next.disabled")) != null;
      } catch (err) {
        const timestamp = Date.now();  // 获取当前时间戳
        const filename = `${timestamp}.jpg`;  // 构建文件名
        await searchResultPage.screenshot({ path: filename });
        logger.error(err);
        await searchResultPage.reload();
        await scrapeSleep();
        await searchResultPage.waitForSelector("a.fp-next", { visible: true });
        hasNext = (await searchResultPage.$("a.fp-next")) != null;
        isLastPage = (await searchResultPage.$("a.fp-next.disabled")) != null;
      }
    }

    for (const sku of skuIdList) {
      const url = `https://item.jd.com/${sku}.html#crumb-wrap`;
      let validSkuIds = await this.getValidSkuIdsFromDetailPage(searchResultPage, url, key_words);
      if (!validSkuIds || validSkuIds.length == 0) {
        logger.info(`过滤${key_words}后, 不存在有效的sku: ${url}`);
        continue;
      }
      // 直接调用 fetchPricesForAllSPUs 来处理这些 SPUs 获取他们的价格信息
      await this.fetchPricesForAllSPUs(searchResultPage, validSkuIds);


      await scrapeSleep();
    }

    // await browser.close();
  };

  async scrapeSearchResultPage(page: Page, key_words: string): Promise<string[] | null> {
    // 获取搜索结果页所有商品item，取到sku id
    const skus = await page.$$eval("li.gl-item", (elements) =>
      elements.map((el) => el.getAttribute("data-sku")),
    );
    const validSkus = skus.filter((sku): sku is string => sku !== null);
    if (validSkus.length == 0) {
      logger.warn(`没有获取到搜索结果页面的skuID`);
      return null;
    } else {
      return validSkus;
    }
  };

  async getValidSkuIdsFromDetailPage(page: Page, url: string, key_words: string) {
    try {
      logger.info(`跳转到商品详情页: ${url}`);
      await page.goto(url, { waitUntil: "networkidle0" });

      // 获取商品详情页面所有的商品sku id
      const spus = await page.$$eval(
        "div#choose-attr-1 div.item",
        (elements, key_words) => {
          const el = elements.filter((el) =>
            el.getAttribute("data-value")?.includes(key_words),
          );
          return el.map((el) => el.getAttribute("data-sku"));
        },
        key_words,
      );

      // 过滤掉可能为 null 的值，确保 spus 数组中的每个元素都是 string 类型
      const validSpus = spus.filter((spu): spu is string => spu !== null);
      return validSpus;

    } catch (err) {
      logger.error(err);
      return null;
    }
  };

  async fetchPricesForAllSPUs(page: Page, spus: string[]) {
    for (const spuId of spus) {
      await this.scrapePrice(page, spuId);
      await scrapeSleep();
    }
    // const scrapePromises = spus.map((spu) => this.scrapePrice(page, spu));
    // // 等待所有详情抓取完成
    // await Promise.all(scrapePromises);
  };

  async scrapePrice(page: Page, spu: string): Promise<any> {
    await scrapeSleep(); // 随机等待4-20s
    const goods: { [key: string]: any } = {};
    const task = await prisma.task.findFirst({});
    goods["taskId"] = task!.id;
    goods["source"] = "JD";
    goods["sku_id"] = spu;
    const subUrl = `https://item.jd.com/${spu}.html#crumb-wrap`;
    logger.info(`获取具体商品型号和价格详情：${subUrl}`);
    await page.goto(subUrl, { waitUntil: "networkidle2" });
    try {
      const sku_name = await page.$eval(
        "div#choose-attr-1 div.item.selected",
        (el) => el.innerText,
      );
      logger.info(`spu_name: ${sku_name}`);
      goods["sku_name"] = sku_name;

      const prices = await page.$$eval(
        "div.summary-price-wrap span.price",
        (el) => el.map((e) => e.textContent).filter((p) => p != ""),
      );
      logger.info(`价格为：${prices}`)
      const resultPrices = prices.filter((p) => p != "");
      if (resultPrices.length == 1) {
        goods["final_price"] = resultPrices[0];
        goods["jd_price"] = resultPrices[0];
      } else {
        goods["final_price"] = resultPrices[0];
        goods["jd_price"] = resultPrices[1];
      }
      logger.info(`Current Price is : ${resultPrices[0]}`);

      const shop_name = await page.$eval("div#summary-service a", (el) =>
        el.innerText.trim(),
      );
      goods["shop_name"] = shop_name;
      logger.info(`shop_name: ${shop_name}`);
      const info = await page.$eval("div.p-parameter", (el) => el.innerText);
      goods["detail"] = info;
      // logger.info(`Detail info: \n ${info}`);
      this.InsertSingleGoods(goods);
    } catch (err) {
      logger.error(`解析网页selector出错，url=${subUrl},error=${err}`);
    }
  };

  InsertSingleGoods = async (goodsData: any) => {
    try {
      const newUser = await prisma.goods.create({
        data: {
          ...goodsData,
        },
      });
      logger.info(`Insert goods: ${JSON.stringify(newUser)}`);
    } catch (error) {
      logger.error(`Error insert goods: ${error}`);
    } finally {
      await prisma.$disconnect();
    }
  };
}
